﻿using UnityEngine;
using System.Collections;
using System;

namespace Helper{
	public class MyTween : MonoBehaviour {
		/// <summary>
		/// 画一条平滑的路径
		/// </summary>
		/// <param name="path">路径节点</param>
		/// <param name="color">路径颜色</param>
		/// <param name="accuracy">节点间差值点数（精度）</param>
		public static void DrawPath(Vector3[] path, int accuracy = 20){
			if (path == null || path.Length <= 0) {
				return;
			}
			Vector3[] vector3s = AddHead_Tail(path);
			//画线
			Vector3 prevPt = Interp(vector3s,0);
			int SmoothAmount = path.Length * accuracy;
			for (int i = 1; i <= SmoothAmount; i++) {
				Vector3 currPt = Interp (vector3s, (float)i / SmoothAmount);
				Gizmos.DrawLine(currPt, prevPt);
				prevPt = currPt;
			}
		}
		/// <summary>
		/// 获取路径中某百分比所在的位置		/// </summary>
		/// <param name="path">Path.</param>
		/// <param name="percentage">Percentage.</param>
		public static Vector3 GetPos(Vector3[] path, float percentage){
			return Interp(AddHead_Tail(path), percentage);
		}
		//在路径首尾增加一点，以便差值
		private static Vector3[] AddHead_Tail(Vector3[] path){
			int offset = 2;
			Vector3[] vector3s = new Vector3[path.Length + offset];
			Array.Copy (path, 0, vector3s, 1, path.Length);
			//路径是个闭环吗？
			if (path [0] == path [path.Length - 1]) {
				vector3s [0] = vector3s [vector3s.Length - 3];
				vector3s [vector3s.Length - 1] = vector3s [2];
			} else {
				vector3s[0] = vector3s[1] + (vector3s[1] - vector3s[2]);
				vector3s[vector3s.Length-1] = vector3s[vector3s.Length-2] + (vector3s[vector3s.Length-2] - vector3s[vector3s.Length-3]);
			}
			return(vector3s);
		}
		//andeeee from the Unity forum's steller Catmull-Rom class ( http://forum.unity3d.com/viewtopic.php?p=218400#218400 ):
		private static Vector3 Interp(Vector3[] pts, float t){
			int numSections = pts.Length - 3;
			int currPt = Mathf.Min(Mathf.FloorToInt(t * (float) numSections), numSections - 1);
			float u = t * (float) numSections - (float) currPt;

			Vector3 a = pts[currPt];
			Vector3 b = pts[currPt + 1];
			Vector3 c = pts[currPt + 2];
			Vector3 d = pts[currPt + 3];

			return .5f * (
				(-a + 3f * b - 3f * c + d) * (u * u * u)
				+ (2f * a - 5f * b + 4f * c - d) * (u * u)
				+ (-a + c) * u
				+ 2f * b
			);
		}

		/// <summary>
		/// 画一条抛物线
		/// </summary>
		/// <returns>The position in parabolic.</returns>
		/// <param name="startPos">抛物线起点</param>
		/// <param name="endPos">抛物线终点</param>
		/// <param name="horizontalVelocity">水平速度</param>
		/// <param name="gravity">重力加速度</param>
		/// <param name="accuracy">精度，即水平方向上单位长度画多少条线</param>
		public static void DrawParabolic(Vector3 startPos, Vector3 endPos, float horizontalVelocity, float gravity, float accuracy = 10f)
		{
			Vector3 diffDir = endPos - startPos;
			Vector3 horizonDir = diffDir;
			horizonDir.y = 0f;
			float totalTime = horizonDir.magnitude / horizontalVelocity;
			int accuracyAmount = (int)(horizonDir.magnitude * accuracy);
			float regionVY = ComputeRegionVelocityInYAxis (startPos, endPos, horizontalVelocity, gravity);
			Vector3 prePos = startPos;
			for (int i = 1; i <= accuracyAmount; i++) {
				float time = totalTime * i / accuracyAmount;
				Vector3 curPos = startPos + horizonDir.normalized * horizontalVelocity * time;
				curPos.y = regionVY * time - 0.5f * gravity * time * time + startPos.y;
				Gizmos.DrawLine (prePos, curPos);
				prePos = curPos;
			}
		}

		/// <summary>
		/// 计算y轴的初始速度
		/// </summary>
		/// <returns>The position in parabolic.</returns>
		/// <param name="startPos">抛物线起点</param>
		/// <param name="endPos">抛物线终点</param>
		/// <param name="horizontalVelocity">水平速度</param>
		/// <param name="gravity">重力加速度</param>
		/// <param name="time">从起点开始经过的时间</param>
		public static float ComputeRegionVelocityInYAxis(Vector3 startPos, Vector3 endPos, float horizontalVelocity, float gravity)
		{
			float V0;
			Vector3 horizonDirection = endPos - startPos;
			float diffY = horizonDirection.y;
			horizonDirection.y = 0f;
			float totalTime = horizonDirection.magnitude / horizontalVelocity;
			V0 = diffY / totalTime + 0.5f * gravity * totalTime;
			return V0;
		}

		/// <summary>
		/// 获取一条抛物线上的某点
		/// </summary>
		/// <returns>The position in parabolic.</returns>
		/// <param name="startPos">抛物线起点</param>
		/// <param name="endPos">抛物线终点</param>
		/// <param name="horizontalVelocity">水平速度</param>
		/// <param name="gravity">重力加速度</param>
		/// <param name="time">从起点开始经过的时间</param>
		/// <param name="isThroughTop">是否经过抛物线顶点</param>
		public static Vector3 GetPosInParabolic(Vector3 startPos, Vector3 endPos, float horizontalVelocity, float gravity, float time)
		{
			Vector3 resultPos;
			Vector3 horizonDirection = endPos - startPos;
			float diffY = horizonDirection.y;
			horizonDirection.y = 0f;
			float totalTime = horizonDirection.magnitude / horizontalVelocity;
			//确定最终的x和z值
			resultPos = startPos + horizonDirection.normalized * horizontalVelocity * time;
			//y=v0*t-0.5gt^2  v0=(diffY)/totalTime + 0.5*g*totalTime
			float V0 = diffY / totalTime + 0.5f * gravity * totalTime;
			resultPos.y = V0 * time - 0.5f * gravity * time * time + startPos.y;
			//if (isThroughTop) {
			//} else {
				//v0*totalTime - 0.5g*totalTime^2 = 2*diffY
			//	float V0 = (2f*diffY + 0.5f*gravity*totalTime*totalTime) / totalTime;
			//	resultPos.y = V0 * time - 0.5f * gravity * time * time;
			//}
			return resultPos;
		}
		//如果要改变重力方向，可以用矩阵

		/// <summary>
		/// 画正弦曲线
		/// </summary>
		/// <param name="startPos">Start position.</param>
		/// <param name="endPos">End position.</param>
		/// <param name="offsetAngle">Offset angle.</param>
		/// <param name="periodNum">Period number.</param>
		public static void DrawSin(Vector3 startPos, Vector3 endPos, float amplitude = 1f, float offsetAngle=0f, float periodNum=0.5f, float startPeriod=0f){
			float accuracy = 10f;
			Vector3 diffDir = endPos - startPos;
			int accuracyAmount = (int)(accuracy * diffDir.magnitude) + 10;
			float tempPercentage = periodNum / accuracyAmount;
			Vector3 prePos = MyTween.GetPosInSin (startPos, endPos, 0f, offsetAngle, periodNum, startPeriod);;
			for (int i = 1; i <= accuracyAmount; i++) {
				Vector3 curPos = MyTween.GetPosInSin (startPos, endPos, i * tempPercentage, amplitude, offsetAngle, periodNum, startPeriod);
				Gizmos.DrawLine (prePos, curPos);
				prePos = curPos;
			}
		}

		/// <summary>
		/// 获取正弦曲线中的位置
		/// </summary>
		/// <returns>The position in sin.</returns>
		/// <param name="startPos">Start position.</param>
		/// <param name="endPos">End position.</param>
		/// <param name="periodPercentage">Period percentage.</param>
		/// <param name="offsetAngle">Offset angle.</param>
		/// <param name="periodNum">Period number.</param>
		/// <param name="startPeriod">Start period.</param>
		public static Vector3 GetPosInSin(Vector3 startPos, Vector3 endPos, float periodPercentage, float amplitude = 1f,
			float offsetAngle=0f, float periodNum=0.5f, float startPeriod=0f){
			Vector3 diffDir = endPos - startPos;
			diffDir /= periodNum;
			Vector3 verticalV = MyTween.GetVerticalVector (diffDir);
			return (diffDir * periodPercentage + verticalV * amplitude * Mathf.Sin ((startPeriod + periodPercentage) * Mathf.PI * 2f) + startPos);
		}

		/// <summary>
		/// 获取输入三维向量的一个垂直向量
		/// 当输入向量不平行于y轴时，返回的向量在yz平面上，且y为正值
		/// 当输入向量平行于y轴时，返回z轴方向
		/// </summary>
		/// <returns>The vertical vector.</returns>
		/// <param name="regionV">Region v.</param>
		public static Vector3 GetVerticalVector(Vector3 regionV){
			const float minDiff = 1e-6f;
			if (Mathf.Abs (regionV.x) <= minDiff && Mathf.Abs (regionV.z) <= minDiff) {
				if (Mathf.Abs (regionV.y) <= minDiff) {
					Debug.LogError ("The Vector3 is zero!");
					return Vector3.zero;
				}
				return new Vector3 (0f, 0f, 1f);
			}
			if (Mathf.Abs (regionV.y) <= minDiff && Mathf.Abs (regionV.z) <= minDiff) {
				return new Vector3 (0f, 1f, 0f);
			}
			Vector3 verticalV = new Vector3 (0f, regionV.z, 0f);
			verticalV.z = -regionV.y;
			return verticalV.normalized;
		}
	}
}
